package ch.unisi.inf.java.project.riskbami.model;

import java.util.ArrayList;
import java.awt.Color;
/**
 * Class Player to handle the players of the game
 * 
 * @author minellir@lu.unisi.ch
 * @author babazadm@lu.unisi.ch
 */
public abstract class Player {
    private final String name;
    private final ArrayList<Country> ownedCountries;
    private final Color color;
    private final ArrayList<Card> ownedCards;
    private int armiesForThisTurn;
    private boolean canDraw;
    private boolean initialPlacing;
    protected boolean isHuman;
    private final ArrayList<PlayerListener> listeners;
    protected Game game;

    
    /**
     * The constructor for the class Player
     * The field armiesForThisTurn is initialized to -1, this is the way to
     * encode the first turn. From the second turn on this value will never be -1.
     * 
     * @param name The name of the player
     * @param color The color that represent the player 
     */
    public Player(final String name, final Color color) {
        this.ownedCountries = new ArrayList<Country>();
        this.ownedCards = new ArrayList<Card>();
        this.name = name;
        this.color = color;
        this.armiesForThisTurn = -1;
        this.canDraw = true;
        this.listeners = new ArrayList<PlayerListener>();
        this.initialPlacing = true;
    }
    
    public final boolean isInitialPlacing(){
        return initialPlacing;
    }
    
    public final boolean isHuman(){
        return isHuman;
    }
    
    public final void noLongerInitialPlacing(){
        this.initialPlacing = false;
    }
    
    /**
     * setGame sets the game for this player
     * 
     * @param game The game
     */
    public final void setGame(final Game game){
        this.game = game;
    }
    
    /**
     * addOwnedCountry adds a country to the ownedCountries list of this player
     * 
     * @param newCountry The country to add
     */
     public final void addOwnedCountry(final Country newCountry){
        ownedCountries.add(newCountry);
    }
    
    /**
     * removeOwnedCountry removes a country to the ownedCountries list
     * 
     * @param oldCountry The country to remove
     */
     public final void removeOwnedCountry(final Country oldCountry){
        ownedCountries.remove(oldCountry);
    }
    
    /**
     * addOwnedCard adds a card to the ownedCards list
     */
     public final void addOwnedCard(){
        ownedCards.add(game.drawCard());
    }
    
    /**
     * addOwnedCards adds a list of cards to the ownedCards list
     */
     public final void addOwnedCards(final ArrayList<Card> cards){
        for(final Card c : cards){
            ownedCards.add(c);
        }
    }
    
    /**
     * setCanDraw is the setter for the boolean field canDraw of the player
     * 
     * @param value The boolean value to assign to the field
     */
    public final void setCanDraw(final boolean value){
        this.canDraw = value;
    }
    
    /**
     * getCanDraw is the accessor for the boolean field canDraw of the player
     * 
     * @return boolean The boolean value of the field canDraw
     */
    public final boolean getCanDraw(){
        return this.canDraw;
    }
    
    /**
     * removeOwnedCard removes a card to the ownedCards list
     * 
     * @param oldCard The card to remove
     */
     public final void removeOwnedCard(final Card oldCard){
        ownedCards.remove(oldCard);
    }
    
    /**
     * useCards provide to exchange a set of cards with a predefined number of armies,
     * if possible accordingly to the original board game rules
     * 
     * @param cards The array of cards that the player wanna exchange
     */
    public final void useCards(final ArrayList<Card> cards){
        int countInf = 0;
        int countCav = 0;
        int countArt= 0;
        int countJol = 0;
        int howManyArmies = 0;
         
        for(final Card c : cards){
            if(c.getType() == CardType.INFANTRY){
                countInf += 1;
            } else if (c.getType() == CardType.ARTILLERY){
                countArt += 1;
            } else if(c.getType() ==  CardType.CAVALRY){
                countCav += 1;
            } else if(c.getType() ==  CardType.JOLLY){
                countJol += 1;
            }
        }
        
        if(countInf == 1 && countArt == 1 && countCav == 1){
            howManyArmies = 10;
        } else if ((countJol == 1 && countArt == 2) || (countJol == 1 && countInf == 2) || (countJol == 1 && countCav == 2)){
            howManyArmies = 12;
        } else if(countInf == 3){
            howManyArmies = 4;
        } else if(countArt == 3){
            howManyArmies = 8;
        } else if(countCav == 3){
            howManyArmies = 6;
        }
        
        if(howManyArmies > 0){
            for(final Card c : cards){
                if(c.getCountry() != null && this.getOwnedCountries().contains(c.getCountry())){
                    howManyArmies += 2;
                }
                //I remove the card from the player's hand i readd to the deck the used card and i shuffle
                this.removeOwnedCard(c);
                game.readdToDeck(c);
            }
            game.shuffleDeck();
            fireTradingSuccessful();
        } else {
            fireTradingError();
        }
        
        this.addPerTurnArmies(howManyArmies);
}
        

    /**
     * getName returns the name of this player
     * 
     * @return String The name of the player
     */
    public final String getName() {
        return name;
    }

    /**
     * getColor returns the color of this player
     * 
     * @return Color The color of the player
     */
    public final Color getColor() {
        return color;
    }
    
    /**
     * getOwnedCards returns the cards of this player
     * 
     * @return ArrayList<Card> The list of cards owned
     */
    public final ArrayList<Card> getOwnedCards() {
        return ownedCards;
    }

    /**
     * getOwnedCountries returns the countries owned by this player
     * 
     * @return ArrayList<Country> The countries owned by this player
     */
    public final ArrayList<Country> getOwnedCountries() {
        return ownedCountries;
    }
    
    /**
     * fortify moves some armies between 2 owned counries, following the rules that at least
     * one army must remain to defend the country from the movement starts.
     * 
     * @param src The country from which the armies moves
     * @param dest The country in which the armies moves
     * @param howMany The number of armies to moves
     */
    public final void fortify(final Country src, final Country dest, final int howMany){
        if(this.ownedCountries.contains(dest) && this.ownedCountries.contains(src) && howMany < src.getNumberOfArmies()) {
            dest.addArmies(howMany);
            src.removeArmies(howMany);
           } else { 
               fireFortifyError(); 
            }
    }
    
    /**
     * attack takes two countries and, if possible, attack.
     * It makes use of the method written in the class Country.
     * It produces automatically the results updating the number of armies of
     * both players in the involved countries. If the defender loose the country
     * it automatically update the lists of owned country of both players and fortify.
     * 
     * @param src The country from which the attack starts
     * @param dest The country in which the attack is made 
     */
    public final void attack(final Country src, final Country dest){
        if(this.ownedCountries.contains(src) && !this.ownedCountries.contains(dest) && src.getNumberOfArmies() > 1) {
            src.attackCountry(dest);
            if(dest.getNumberOfArmies() == 0){
                final Player oldOwner = game.getOwnerOf(dest);
                oldOwner.removeOwnedCountry(dest);
                this.addOwnedCountry(dest);
                fireCountryLost(oldOwner, this);
                fireCountryConquer(dest, this);
                if(!this.isHuman()){
                    if(src.getNumberOfArmies() > 4){
                        this.fortify(src, dest, 3 + new java.util.Random().nextInt(src.getNumberOfArmies() - 4));
                    } else {
                        this.fortify(src, dest, Math.min(3, src.getNumberOfArmies() - 1));
                    }
                }
                if(this.getCanDraw()){
                    this.addOwnedCard();
                    this.setCanDraw(false);
                }
            }
        } else { 
            fireAttackError(); 
        }
    }
    
    /**
     * computePerTurnArmies compute the number of armies that you can place this turn accordingly to the
     * eventual bonuses and the number of countries divided by 3.
     */
    public final void computePerTurnArmies(){
        final int numberOwnedCountries = this.ownedCountries.size();
        this.armiesForThisTurn = numberOwnedCountries / 3;
    }
    
    /**
     * setFirstTurnArmies compute the number of armies that you can place the first turn accordingly
     * with the number of players involved in this game
     * 
     * @param numberOfPlayers The number of players involved
     */
    public final void setFirstTurnArmies(final int numberOfPlayers){
        if(numberOfPlayers < 2 || numberOfPlayers > 6){
            System.err.println("Number of players out of range");
            return;
        }
        this.armiesForThisTurn = 20;
          for(int i=6; i>numberOfPlayers; i--){
            this.armiesForThisTurn += 5;
          }
    }
    
    /**
     * getPerTurnArmies return the number of armies that you can place this turn
     * 
     * @return int The number of available armies to place
     */
    public final int getPerTurnArmies(){
        return this.armiesForThisTurn;
    }
    
    /**
     * addPerTurnArmies add the number of armies that you can place this turn
     * 
     * @param number The number of armies to add
     */
    public final void addPerTurnArmies(final int number){
        this.armiesForThisTurn += number;
    }
    
    /**
     * removePerTurnArmies remove the given number from the armies that you can place this turn
     * 
     * @param howMany The number of armies to remove
     */
    public final void removePerTurnArmies(final int howMany){
        this.armiesForThisTurn -= howMany;
    }
    
    /**
     * toString implementation for the class Player
     * 
     * @return String The name of this player
     */
    public final String toString(){
        return this.getName();
    }
    
    /**
     * The following methods add and remove listeners for country and players.
     */
    
    public void addPlayerListener(final PlayerListener li){
        listeners.add(li);
    }
    
    public void removePlayerListener(final PlayerListener li){
        listeners.remove(li);
    }
    
    public void fireCountryConquer(Country conquer, Player conquistadores){
        for(final PlayerListener li : listeners){
            li.countryConquer(conquer, conquistadores);
        }
    }
    
    public void fireCountryLost(Player old, Player niu){
        for(final PlayerListener li : listeners){
            li.countryLost(old, niu); 
        }
    }
    
    public void fireAttackError(){
        for(final PlayerListener li : listeners){
            li.attackError(); 
        }
    }
    
    public void fireFortifyError(){
        for(final PlayerListener li : listeners){
            li.fortifyError(); 
        }
    }
    
    public void fireTradingError(){
        for(final PlayerListener li : listeners){
            li.tradingError(); 
        }
    }
    
    public void fireTradingSuccessful(){
        for(final PlayerListener li : listeners){
            li.tradingSuccessful(); 
        }
    }
    
    public void playTurn(){
    }
}
